#ifndef BLOCK_HH
#define BLOCK_HH

#define LOADED	(1<<0)		//If page is to be read from disk
#define DIRTY	(1<<1)		//If page is dirty and to be flushed out
#define PIN	(1<<2)		//If page is to be pinned to memory and not to be flushed out


#define set_bit(x,y)	(x|=y)
#define reset_bit(x,y)	(x&=(~y))
#define get_bit(x,y)	((x&y)!=0)

class block {
	char flags;
	char file_name[FILE_NAME_LENGTH];
	int block_num;		//No. of the block in the file for this table
	int num_attribs;	//No. of attribs in the table for this block.
	int max_records;	//Total number of records that can be stored
	int records_count;	//No. of records stored in this particular block
	char data[BLOCK_LENGTH];
	int disk_io;

public:
	int *buffer;
	class stm* stm_ptr;
	
	int initialize(char *file_name, int numattribs, int blocknum, int numrecords, stm* stm1);
	int insert_record(int *arr);
	void flush();
	void free();

	/*** General get function to get the data items ***/
	int   loaded()			{ return((flags&LOADED)!=0);	}
	void  pin()			{ set_bit(flags, PIN);		}
	void  unpin()			{ reset_bit(flags, PIN);	}
	int   pinned()			{ return(get_bit(flags, PIN));	}
	int   get_block_no()		{ return(block_num);	}
	int   no_of_attribs()		{ return(num_attribs);	}
	int   records_present()		{ return(records_count);}
	int   num_record_slots()	{ return(max_records);	}
	char* get_file_name()		{ return(file_name);	}
	int   get_data_at(int record, int index);
	int   is_diskio()		{ return(disk_io);	}

	/*** Print function to print the data in records ***/
	void print_row(int rec_no);
	int* get_row(int rec_no);
	int print_records();
	int print_records(int num, int *attrib_map);
	int print();
};

int* block :: get_row(int rec_no)
{
	if(rec_no < records_count)
		return(&buffer[rec_no*num_attribs]);
	else
		return(NULL);
}

void block :: print_row(int rec_no)
{
	int* aRow = &buffer[rec_no*num_attribs];
	int c=0;

	for(c=0; c<num_attribs; c++)
	{
		PRINT("%8d ", aRow[c]);
	}
	PRINT("\n");
}

int validate_cquery(condition *qry)
{
	int status=1;
	int val1=0, val2=0;

	if(NULL == qry)
		return (0==0);

	switch(qry->isfield1) {
		case ISATTRIB_TYPE:
		case ISTATTRIB_TYPE:
		case ISNUMBER_TYPE:
			val1 = ((attrib_field *)qry->f1)->value;
			break;

		case ISCONDITION_TYPE:
			val1 = validate_cquery((condition *) qry->f1);
			break;

		case ISOPERATOR_TYPE:
		case ISAGGREGATE_TYPE:
		default:
			FATAL("Invalid type in field %d \n", qry->isfield2);
			status = 1;
	};

	switch(qry->isfield2) {
		case ISATTRIB_TYPE:
		case ISTATTRIB_TYPE:
		case ISNUMBER_TYPE:
			val2 = ((attrib_field *)qry->f2)->value;
			break;

		case ISCONDITION_TYPE:
			val2 = validate_cquery((condition *) qry->f2);
			break;

		case ISOPERATOR_TYPE:
		case ISAGGREGATE_TYPE:
		default:
			FATAL("Invalid type in field %d \n", qry->isfield2);
			status = 1;
	};
	
	switch(qry->op) {
		case OP_GR:
			status = (val1 > val2);
			break;
			
		case OP_LS:
			status = (val1 < val2);
			break;
			
		case OP_EQ:
			status = (val1 == val2);
			break;
			
		case OP_GEQ:
			status = (val1 >= val2);
			break;
			
		case OP_LEQ:
			status = (val1 <= val2);
			break;
		case OP_AND:
			status = (val1 & val2);
			break;
		default:
			FATAL("Invalid Operand %d \n", qry->op);
			status = 1;
	}

	DBGPRINTN(status);
	return(status);
			
}

int block :: initialize(char *tablename, int numattribs, int blocknum, int numrecords, stm* stm1)
{
	int i=0;
	buffer = (int *) data;

	if(!loaded()) {

		DBGPRINTF("tablename: %s\n",tablename);
		DBGPRINTF("blocknum: %d\n",blocknum);

		strcpy(file_name, tablename);
		num_attribs = numattribs;
		block_num = blocknum;
		stm_ptr = stm1;
		max_records = BLOCK_LENGTH/(num_attribs * sizeof(int));
		records_count=numrecords;

		if(0 != records_count)	stm1->read_block(blocknum, (char *)data);
		if(0 == records_count) 	for(i=0; i<(BLOCK_LENGTH/4); i++) *((buffer)+i)=-1;

		set_bit(flags, LOADED);
		DBGPRINTF("Loaded the page and set the bit LOADED\n");
		disk_io = 1;
	} else {
		disk_io = 0;
	}

	return(0);
}

int block :: insert_record(int *arr)
{
	set_bit(flags, DIRTY);

	if (records_count<max_records)
	{
		//???? Is there any need to ascending or descending order
		int i=0,j=(num_attribs*records_count);
		int* buff= (int *)data;

		for(i=0;i<num_attribs;i++,j++)
		{
			buff[j]=arr[i];
			DBGPRINTN(buff[j]);
		}
		DBGPRINTF("###### %-4s BLOCK #%4d, RECORD #%4d(%4d) ######\n", file_name, block_num, records_count, max_records);
		records_count++;
		DBGMEMPRINT(data, BLOCK_LENGTH);
		return(0);
	}
	else
	{
		DBGPRINTF("Inserting Record into Block #%d of %s that is already full!!! \n", block_num, file_name);
		return(1);
	}
}

int block :: get_data_at(int record, int index)
{
	int *aRow = get_row(record);

	if(record > max_records || index > num_attribs)
	{
		FATAL("Invalid record number %d > %d or Index %d > %d\n", record, max_records, index, num_attribs);
		return (-1);
	}
	else
	{
		DBGPRINTF("data[%d][%d] = %d >>> ", record, index, aRow[index]);
		return(aRow[index]);
	}
}

int block :: print_records()
{
	print_records(num_attribs, NULL);
}

int block :: print_records(int num,int *attrib_map)
{
	int r = 0, c = 0;
	int *aRow;

	PRINT("\n");
	DBGPRINTN(records_count);
	for(r=0; r<records_count; r++)
	{
		aRow = &buffer[r*num_attribs];
		for(c=0; c<num; c++)
		{
			if(NULL == attrib_map)
				PRINT("%8d ", aRow[c]);
			else
				PRINT("%8d ", aRow[attrib_map[c]]);
		}
		PRINT("\n");
	}

	return(0);
}

int block :: print()
{
	if(!loaded())
		PRINT("\t%-8s","!!! EMPTY !!!");
	else
		PRINT("\t%-8s\t%-8d",file_name, block_num);

	return(0);
}
	
void block :: flush()
{
	stm_ptr->write_block(block_num, (char *) data);
	reset_bit(flags, DIRTY);
	DBGPRINTN(flags);
}

void block :: free()
{
	int i=0;
	int *buf = (int *) data;

	flush();
	flags=0;
	num_attribs = 0;
	block_num = 0;
	stm_ptr = NULL;
	max_records = 0;
	records_count = 0;
	for(i=0; i<(BLOCK_LENGTH/4); i++)	*((buf)+i)=-1;
}


#endif
